"""
WebSocket监控和告警系统
=======================

实现完整的监控告警功能：
1. 实时性能监控
2. 告警规则配置
3. 告警触发和通知
4. 健康检查和自动恢复
"""

import asyncio
import logging
from typing import Dict, List, Optional, Any, Callable
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
from collections import defaultdict, deque

logger = logging.getLogger(__name__)


class AlertLevel(Enum):
    """告警级别"""
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


class MetricType(Enum):
    """指标类型"""
    COUNTER = "counter"
    GAUGE = "gauge"
    HISTOGRAM = "histogram"
    RATE = "rate"


@dataclass
class Alert:
    """告警信息"""
    rule_name: str
    level: AlertLevel
    message: str
    metric_name: str
    metric_value: float
    threshold: float
    timestamp: datetime = field(default_factory=datetime.now)
    resolved: bool = False
    resolved_at: Optional[datetime] = None
    
    def resolve(self):
        """解决告警"""
        self.resolved = True
        self.resolved_at = datetime.now()
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "rule_name": self.rule_name,
            "level": self.level.value,
            "message": self.message,
            "metric_name": self.metric_name,
            "metric_value": self.metric_value,
            "threshold": self.threshold,
            "timestamp": self.timestamp.isoformat(),
            "resolved": self.resolved,
            "resolved_at": self.resolved_at.isoformat() if self.resolved_at else None
        }


@dataclass
class AlertRule:
    """告警规则"""
    name: str
    metric_name: str
    level: AlertLevel
    condition: Callable[[float, float], bool]  # (value, threshold) -> bool
    threshold: float
    window_seconds: int = 60  # 时间窗口（秒）
    min_occurrences: int = 1  # 最小触发次数
    cooldown_seconds: int = 300  # 冷却时间（秒）
    enabled: bool = True
    
    # 运行时状态
    last_triggered: Optional[datetime] = None
    occurrence_count: int = 0
    
    def can_trigger(self) -> bool:
        """检查是否可以触发告警（考虑冷却时间）"""
        if not self.enabled:
            return False
        
        if self.last_triggered is None:
            return True
        
        elapsed = (datetime.now() - self.last_triggered).total_seconds()
        return elapsed >= self.cooldown_seconds
    
    def evaluate(self, value: float) -> bool:
        """评估规则"""
        return self.condition(value, self.threshold)


class MetricCollector:
    """
    指标收集器
    
    收集和存储各种性能指标
    """
    
    def __init__(self, retention_seconds: int = 3600):
        """
        初始化指标收集器
        
        Args:
            retention_seconds: 数据保留时间（秒）
        """
        self.retention_seconds = retention_seconds
        
        # 指标存储：{metric_name: [(timestamp, value), ...]}
        self.metrics: Dict[str, deque] = defaultdict(lambda: deque(maxlen=10000))
        
        logger.info(f"MetricCollector initialized: retention={retention_seconds}s")
    
    def record(self, metric_name: str, value: float, timestamp: Optional[datetime] = None):
        """
        记录指标值
        
        Args:
            metric_name: 指标名称
            value: 指标值
            timestamp: 时间戳（可选）
        """
        if timestamp is None:
            timestamp = datetime.now()
        
        self.metrics[metric_name].append((timestamp, value))
        
        # 清理过期数据
        self._cleanup_expired(metric_name)
    
    def _cleanup_expired(self, metric_name: str):
        """清理过期数据"""
        now = datetime.now()
        cutoff = now - timedelta(seconds=self.retention_seconds)
        
        metrics = self.metrics[metric_name]
        while metrics and metrics[0][0] < cutoff:
            metrics.popleft()
    
    def get_current(self, metric_name: str) -> Optional[float]:
        """获取当前值"""
        metrics = self.metrics.get(metric_name)
        if not metrics:
            return None
        return metrics[-1][1]
    
    def get_average(self, metric_name: str, window_seconds: int = 60) -> Optional[float]:
        """获取平均值"""
        metrics = self.metrics.get(metric_name)
        if not metrics:
            return None
        
        now = datetime.now()
        cutoff = now - timedelta(seconds=window_seconds)
        
        values = [value for ts, value in metrics if ts >= cutoff]
        if not values:
            return None
        
        return sum(values) / len(values)
    
    def get_rate(self, metric_name: str, window_seconds: int = 60) -> Optional[float]:
        """获取速率（每秒变化）"""
        metrics = self.metrics.get(metric_name)
        if not metrics:
            return None
        
        now = datetime.now()
        cutoff = now - timedelta(seconds=window_seconds)
        
        filtered = [(ts, value) for ts, value in metrics if ts >= cutoff]
        if len(filtered) < 2:
            return None
        
        # 计算速率
        first_ts, first_value = filtered[0]
        last_ts, last_value = filtered[-1]
        
        elapsed = (last_ts - first_ts).total_seconds()
        if elapsed == 0:
            return None
        
        return (last_value - first_value) / elapsed
    
    def get_max(self, metric_name: str, window_seconds: int = 60) -> Optional[float]:
        """获取最大值"""
        metrics = self.metrics.get(metric_name)
        if not metrics:
            return None
        
        now = datetime.now()
        cutoff = now - timedelta(seconds=window_seconds)
        
        values = [value for ts, value in metrics if ts >= cutoff]
        if not values:
            return None
        
        return max(values)
    
    def get_min(self, metric_name: str, window_seconds: int = 60) -> Optional[float]:
        """获取最小值"""
        metrics = self.metrics.get(metric_name)
        if not metrics:
            return None
        
        now = datetime.now()
        cutoff = now - timedelta(seconds=window_seconds)
        
        values = [value for ts, value in metrics if ts >= cutoff]
        if not values:
            return None
        
        return min(values)


class WebSocketMonitor:
    """
    WebSocket监控系统
    
    功能：
    - 指标收集
    - 告警规则管理
    - 告警触发和通知
    - 健康检查
    """
    
    def __init__(
        self,
        check_interval_seconds: int = 10,
        retention_seconds: int = 3600
    ):
        """
        初始化监控系统
        
        Args:
            check_interval_seconds: 检查间隔（秒）
            retention_seconds: 数据保留时间（秒）
        """
        self.check_interval_seconds = check_interval_seconds
        
        # 指标收集器
        self.collector = MetricCollector(retention_seconds=retention_seconds)
        
        # 告警规则
        self.alert_rules: Dict[str, AlertRule] = {}
        
        # 活跃告警
        self.active_alerts: Dict[str, Alert] = {}
        
        # 告警历史
        self.alert_history: deque = deque(maxlen=1000)
        
        # 告警回调
        self.alert_callbacks: List[Callable[[Alert], None]] = []
        
        # 后台任务
        self.monitor_task: Optional[asyncio.Task] = None
        self.is_running = False
        
        # 初始化默认告警规则
        self._init_default_rules()
        
        logger.info(f"WebSocketMonitor initialized: check_interval={check_interval_seconds}s")
    
    def _init_default_rules(self):
        """初始化默认告警规则"""
        # 连接断开率告警
        self.add_rule(AlertRule(
            name="high_disconnection_rate",
            metric_name="websocket.disconnection_rate",
            level=AlertLevel.WARNING,
            condition=lambda v, t: v > t,
            threshold=0.1,  # 10%断开率
            window_seconds=60,
            cooldown_seconds=300
        ))
        
        # 连接数过高告警
        self.add_rule(AlertRule(
            name="connection_pool_utilization_high",
            metric_name="websocket.pool_utilization",
            level=AlertLevel.WARNING,
            condition=lambda v, t: v > t,
            threshold=0.9,  # 90%使用率
            window_seconds=60,
            cooldown_seconds=300
        ))
        
        # 消息延迟告警
        self.add_rule(AlertRule(
            name="high_message_latency",
            metric_name="websocket.message_latency_ms",
            level=AlertLevel.ERROR,
            condition=lambda v, t: v > t,
            threshold=1000,  # 1秒延迟
            window_seconds=60,
            cooldown_seconds=300
        ))
        
        # 错误率告警
        self.add_rule(AlertRule(
            name="high_error_rate",
            metric_name="websocket.error_rate",
            level=AlertLevel.ERROR,
            condition=lambda v, t: v > t,
            threshold=0.05,  # 5%错误率
            window_seconds=60,
            cooldown_seconds=300
        ))
    
    async def start(self):
        """启动监控"""
        if self.is_running:
            logger.warning("Monitor already running")
            return
        
        self.is_running = True
        self.monitor_task = asyncio.create_task(self._monitor_loop())
        
        logger.info("WebSocketMonitor started")
    
    async def stop(self):
        """停止监控"""
        self.is_running = False
        
        if self.monitor_task:
            self.monitor_task.cancel()
            try:
                await self.monitor_task
            except asyncio.CancelledError:
                pass
        
        logger.info("WebSocketMonitor stopped")
    
    async def _monitor_loop(self):
        """监控循环"""
        while self.is_running:
            try:
                await asyncio.sleep(self.check_interval_seconds)
                await self._check_alerts()
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Monitor loop error: {e}", exc_info=True)
    
    async def _check_alerts(self):
        """检查所有告警规则"""
        for rule_name, rule in self.alert_rules.items():
            if not rule.enabled:
                continue
            
            # 获取指标值
            value = self.collector.get_average(rule.metric_name, rule.window_seconds)
            if value is None:
                continue
            
            # 评估规则
            if rule.evaluate(value):
                # 增加触发次数
                rule.occurrence_count += 1
                
                # 检查是否达到最小触发次数
                if rule.occurrence_count >= rule.min_occurrences and rule.can_trigger():
                    await self._trigger_alert(rule, value)
                    rule.last_triggered = datetime.now()
                    rule.occurrence_count = 0
            else:
                # 重置触发次数
                rule.occurrence_count = 0
                
                # 解决告警（如果存在）
                if rule_name in self.active_alerts:
                    await self._resolve_alert(rule_name)
    
    async def _trigger_alert(self, rule: AlertRule, value: float):
        """触发告警"""
        message = (
            f"Alert: {rule.name} - {rule.metric_name} = {value:.2f} "
            f"(threshold: {rule.threshold})"
        )
        
        alert = Alert(
            rule_name=rule.name,
            level=rule.level,
            message=message,
            metric_name=rule.metric_name,
            metric_value=value,
            threshold=rule.threshold
        )
        
        # 添加到活跃告警
        self.active_alerts[rule.name] = alert
        
        # 添加到历史
        self.alert_history.append(alert)
        
        logger.warning(f"Alert triggered: {message}")
        
        # 调用告警回调
        for callback in self.alert_callbacks:
            try:
                callback(alert)
            except Exception as e:
                logger.error(f"Alert callback error: {e}", exc_info=True)
    
    async def _resolve_alert(self, rule_name: str):
        """解决告警"""
        if rule_name in self.active_alerts:
            alert = self.active_alerts[rule_name]
            alert.resolve()
            del self.active_alerts[rule_name]
            
            logger.info(f"Alert resolved: {rule_name}")
    
    def add_rule(self, rule: AlertRule):
        """添加告警规则"""
        self.alert_rules[rule.name] = rule
        logger.info(f"Alert rule added: {rule.name}")
    
    def remove_rule(self, rule_name: str):
        """移除告警规则"""
        if rule_name in self.alert_rules:
            del self.alert_rules[rule_name]
            logger.info(f"Alert rule removed: {rule_name}")
    
    def enable_rule(self, rule_name: str):
        """启用告警规则"""
        if rule_name in self.alert_rules:
            self.alert_rules[rule_name].enabled = True
    
    def disable_rule(self, rule_name: str):
        """禁用告警规则"""
        if rule_name in self.alert_rules:
            self.alert_rules[rule_name].enabled = False
    
    def add_alert_callback(self, callback: Callable[[Alert], None]):
        """添加告警回调"""
        self.alert_callbacks.append(callback)
    
    def record_metric(self, metric_name: str, value: float):
        """记录指标"""
        self.collector.record(metric_name, value)
    
    def get_active_alerts(self) -> List[Dict[str, Any]]:
        """获取活跃告警"""
        return [alert.to_dict() for alert in self.active_alerts.values()]
    
    def get_alert_history(self, limit: int = 100) -> List[Dict[str, Any]]:
        """获取告警历史"""
        history = list(self.alert_history)[-limit:]
        return [alert.to_dict() for alert in history]
    
    def get_rules(self) -> List[Dict[str, Any]]:
        """获取所有规则"""
        return [{
            "name": rule.name,
            "metric_name": rule.metric_name,
            "level": rule.level.value,
            "threshold": rule.threshold,
            "window_seconds": rule.window_seconds,
            "enabled": rule.enabled,
            "last_triggered": rule.last_triggered.isoformat() if rule.last_triggered else None
        } for rule in self.alert_rules.values()]
    
    def get_health_status(self) -> Dict[str, Any]:
        """获取健康状态"""
        active_critical = sum(
            1 for alert in self.active_alerts.values() 
            if alert.level == AlertLevel.CRITICAL
        )
        active_errors = sum(
            1 for alert in self.active_alerts.values() 
            if alert.level == AlertLevel.ERROR
        )
        active_warnings = sum(
            1 for alert in self.active_alerts.values() 
            if alert.level == AlertLevel.WARNING
        )
        
        # 判断整体健康状态
        if active_critical > 0:
            status = "critical"
        elif active_errors > 0:
            status = "error"
        elif active_warnings > 0:
            status = "warning"
        else:
            status = "healthy"
        
        return {
            "status": status,
            "active_alerts": len(self.active_alerts),
            "active_critical": active_critical,
            "active_errors": active_errors,
            "active_warnings": active_warnings,
            "total_rules": len(self.alert_rules),
            "enabled_rules": sum(1 for rule in self.alert_rules.values() if rule.enabled),
            "is_running": self.is_running
        }


# 全局监控实例
_monitor: Optional[WebSocketMonitor] = None


def get_websocket_monitor(**kwargs) -> WebSocketMonitor:
    """获取全局监控实例"""
    global _monitor
    if _monitor is None:
        _monitor = WebSocketMonitor(**kwargs)
    return _monitor

